package com.zhufeng.web.socket;

import com.alibaba.fastjson.JSONObject;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import java.net.InetSocketAddress;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * @ClassName: WebSocketHandle
 * @Description TODO
 * @author 月夜烛峰
 * @date 2022/9/23 09:35
 */
@Slf4j
public class WebSocketHandle extends SimpleChannelInboundHandler<Object> {

    //客户端组
    public static ChannelGroup channelGroup;

    static {
        channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    }

    /**
     * 存储 Channel 的容器
     */
    private static ConcurrentMap<String, Channel> channelMap = new ConcurrentHashMap<>();


    /**
     * Handler活跃状态，连接成功
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 将通道放入组
        channelGroup.add(ctx.channel());
    }

    /**
     * 通道读取
     */

    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {

        //log.info("类型:"+msg.getClass());
        log.info(JSONObject.toJSONString(msg));
        //WebSocketFrame frame = (WebSocketFrame) msg;
        //log.info("frame类型:"+frame.getClass());

        // 文本消息 (约定 与客户端 Socket 消息类型为文本消息)
        //if (msg instanceof TextWebSocketFrame) {
            // 获取当前channel绑定的IP地址
            InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
            String address = ipSocket.getAddress().getHostAddress();
            log.info("获取到远程连接：IP：{}", address);

            //TextWebSocketFrame textFrame = (TextWebSocketFrame) msg;
            //String message = textFrame.text();
            //log.info("收到来自客户端消息: {}", message);

/**
            // 此处 NettyMsg 类为自定义消息解析器，通过 message 串来解析消息。
            NettyMessage nettyMessage = NettyMsg.getNettyMessage(message);
            // 解析成为自定义消息对象
            SysSocketUser sysSocketUser = NettyMsg.getCon(nettyMessage.getCon(), SysSocketUser.class);

            // 与客户端约定建立连接成功时，发送一条包含用户ID的消息，此处从消息中获取用户ID与Channel绑定，后续为用户发送消息时使用。
            if (CheckUtil.objIsNotEmpty(sysSocketUser)) {
                if (CheckUtil.objIsNotEmpty(sysSocketUser.getUserId())) {
                    //将 用户 和 Channel 的关系保存
                    if (!channelMap.containsKey(sysSocketUser.getUserId())) {
                        channelMap.put(sysSocketUser.getUserId(), ctx.channel());
                    }
                }
            }
 */

        //}
        // PING 类型消息
        if (msg instanceof PongWebSocketFrame) {
            log.info("PING SUCCESS");
        }
        // 请求关闭连接类型消息
        if (msg instanceof CloseWebSocketFrame) {
            log.info("客户端关闭连接，服务端关闭通道");
            Channel channel = ctx.channel();
            channel.close();
        }
    }

    /**
     * 未注册状态
     */
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        log.info("等待连接");
    }

    /**
     * 非活跃状态，没有连接远程主机的时候。
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("客户端关闭");
        channelGroup.remove(ctx.channel());
    }

    /**
     * 异常处理
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("连接异常：" + cause.getMessage());
        ctx.close();
    }

    /**
     * 给指定用户发内容
     * 可以使用此方法推送消息给客户端
     */
    public void sendMessage(String userId, String message) {
        Channel channel = channelMap.get(userId);
        channel.writeAndFlush(new TextWebSocketFrame(message));
    }

    /**
     * 群发消息
     */
    public void sendMessageAll(String message) {
        channelGroup.writeAndFlush(new TextWebSocketFrame(message));
    }

    @Override
    protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {

        log.info("类型:"+msg.getClass());
        log.info(JSONObject.toJSONString(msg));
        WebSocketFrame frame = (WebSocketFrame) msg;
        log.info("frame类型:"+frame.getClass());

        // 文本消息 (约定 与客户端 Socket 消息类型为文本消息)
        //if (msg instanceof TextWebSocketFrame) {
        // 获取当前channel绑定的IP地址
        InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String address = ipSocket.getAddress().getHostAddress();
        log.info("获取到远程连接：IP：{}", address);

        //TextWebSocketFrame textFrame = (TextWebSocketFrame) msg;
        //String message = textFrame.text();
        //log.info("收到来自客户端消息: {}", message);

/**
 // 此处 NettyMsg 类为自定义消息解析器，通过 message 串来解析消息。
 NettyMessage nettyMessage = NettyMsg.getNettyMessage(message);
 // 解析成为自定义消息对象
 SysSocketUser sysSocketUser = NettyMsg.getCon(nettyMessage.getCon(), SysSocketUser.class);

 // 与客户端约定建立连接成功时，发送一条包含用户ID的消息，此处从消息中获取用户ID与Channel绑定，后续为用户发送消息时使用。
 if (CheckUtil.objIsNotEmpty(sysSocketUser)) {
 if (CheckUtil.objIsNotEmpty(sysSocketUser.getUserId())) {
 //将 用户 和 Channel 的关系保存
 if (!channelMap.containsKey(sysSocketUser.getUserId())) {
 channelMap.put(sysSocketUser.getUserId(), ctx.channel());
 }
 }
 }
 */

        //}
        // PING 类型消息
        if (msg instanceof PongWebSocketFrame) {
            log.info("PING SUCCESS");
        }
        // 请求关闭连接类型消息
        if (msg instanceof CloseWebSocketFrame) {
            log.info("客户端关闭连接，服务端关闭通道");
            Channel channel = ctx.channel();
            channel.close();
        }
    }
}

